﻿using System;
using System.Text;

namespace FuzzScript
{
    class FuzzyVariable : Node
    {
        internal static int Resolution
        {
            get { return 500;}
        }

        double m_dVariableValue;

        public double Value
        {
            get { return m_dVariableValue; }
            set 
            { 
                m_dVariableValue = value;

                int iDiscreteVal = VariableToDiscreteIndex(m_dVariableValue);

                for (int i = 0; i < m_SiblingNodes.Count; i++)
                {
                    ((FuzzySet)m_SiblingNodes[i]).SetValue(iDiscreteVal);
                }
            }
        }

        double m_dLowerLimit;

        public double LowerLimit
        {
            get { return m_dLowerLimit; }
            set { m_dLowerLimit = value; }
        }

        double m_dUpperLimit;

        public double UpperLimit
        {
            get { return m_dUpperLimit; }
            set { m_dUpperLimit = value; }
        }

        private double m_dLimitDifference;

        #region #region initialization and display

        private FuzzyVariable() { }

        internal FuzzyVariable(string p_sName,double p_dLowLim, double p_dUpLim, bool p_bIsInput)
        {
            m_sName = p_sName;
            m_dLowerLimit = p_dLowLim;
            m_dUpperLimit = p_dUpLim;

            m_dLimitDifference = (m_dUpperLimit - m_dLowerLimit) / Resolution;

            if(!p_bIsInput)
            {
                if (!SymbolTable.Instance.Contains(SymbolTable.AttributeOutputVariableSet(p_sName)))
                {
                    SymbolTable.Instance.Put(SymbolTable.AttributeOutputVariableSet(p_sName), 
                        new FuzzySet(p_sName));
                }
            }

           SymbolTable.Instance.Put(p_sName, this);

        }

        public override string ToString()
        {
            return "VARIABLE{ " + m_sName + " }";
        }
        #endregion
        
        public override void AddSibling(INode p_SiblingNode)
        {
            base.AddSibling(p_SiblingNode);

            SymbolTable.Instance.Put(SymbolTable.AttributeFuzzySet(m_sName
                , ((FuzzySet)p_SiblingNode).Name)
                , p_SiblingNode);
        }

        internal double GetOutputValue()
        {
            return ResolutionToVariable
                (((FuzzySet)SymbolTable.Instance.Get(
                    SymbolTable.AttributeOutputVariableSet(m_sName))).GetValueIndex());
        }

        /// <summary>
        /// Converts 
        /// </summary>
        /// <param name="iVal"></param>
        /// <returns></returns>
        private double ResolutionToVariable(int p_iVal)
        {
            if (p_iVal < 0 && p_iVal > (FuzzyVariable.Resolution))
            {
                p_iVal = 0;
            }

            return (((double)p_iVal / (double)(FuzzyVariable.Resolution)) * (m_dUpperLimit - m_dLowerLimit)) + m_dLowerLimit;
        }

        public int VariableToDiscreteIndex(double p_dVal)
        {
            // check if the value is beyond the variable limits, if so adjust to the limit
            if (p_dVal > m_dUpperLimit) 
                p_dVal = m_dUpperLimit;

            if (p_dVal < m_dLowerLimit)
                p_dVal = m_dLowerLimit;

            // map the value and return
            return (int)((p_dVal - m_dLowerLimit) / m_dLimitDifference);
        }

        /// <summary>
        /// Variables do not implement an Execute method
        /// </summary>
        /// <returns></returns>
        public override double Execute()
        {
            throw new NotImplementedException();
        }

        public override string Generate()
        {
            StringBuilder sCode = new StringBuilder();

            // for each variable, irrispective of its nature, the following attributes
            // must be added:

            // the variable lower limit
            sCode.Append("double " + SymbolTable.AttributeVariableLowerLimit(Name)
                + " = " + this.m_dLowerLimit + ";\n");

            // the variable upper limit
            sCode.Append("double " + SymbolTable.AttributeVariableUpperLimit(Name)
                + " = " + this.m_dUpperLimit + ";\n");

 
            for (int i = 0; i < m_SiblingNodes.Count; i++)
            {
                sCode.Append("\ndouble[] " +
                    SymbolTable.AttributeFuzzySet(Name,
                    ((FuzzySet)m_SiblingNodes[i]).Name)
                    + " =  " + ((FuzzySet)m_SiblingNodes[i]).Generate() + ";\n");
            }
            return sCode.ToString();
        }
    }
}
